/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Eclipse Public License (EPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/eclipse-1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.mmtk.plan.compressor;

import org.mmtk.plan.*;
import org.mmtk.policy.Space;
import org.mmtk.policy.compressor.MetadataComputingScanner;
import org.mmtk.policy.compressor.MarkCopyLocal;
import org.mmtk.utility.Log;
import org.mmtk.utility.alloc.Allocator;
import org.mmtk.utility.alloc.BumpPointer;
import org.mmtk.utility.alloc.LinearScan;
import org.mmtk.vm.VM;

import org.vmmagic.unboxed.*;
import org.vmmagic.pragma.*;

/**
 * This class implements <i>per-mutator thread</i> behavior
 * and state for the <i>SS</i> plan, which implements a full-heap
 * semi-space collector.<p>
 *
 * Specifically, this class defines <i>SS</i> mutator-time allocation
 * and per-mutator thread collection semantics (flushing and restoring
 * per-mutator allocator state).<p>
 *
 * See {@link Compressor} for an overview of the semi-space algorithm.<p>
 *
 * @see Compressor
 * @see CompressorCollector
 * @see StopTheWorldMutator
 * @see MutatorContext
 */
@Uninterruptible
public class CompressorMutator extends StopTheWorldMutator {
  
  public static boolean VERBOSE = true;
  
  /****************************************************************************
   * Instance fields
   */
  protected final MarkCopyLocal markCopyLocal;
  
  /****************************************************************************
   *
   * Initialization
   */

  /**
   * Constructor
   */
  public CompressorMutator() {
    markCopyLocal = new MarkCopyLocal();
  }

  /**
   * Called before the MutatorContext is used, but after the context has been
   * fully registered and is visible to collection.
   */
  public void initMutator(int id) {
    super.initMutator(id);
    markCopyLocal.rebind(Compressor.toSpace());
  }

  /**
   * The mutator is about to be cleaned up, make sure all local data is returned.
   */
  @Override
  public void deinitMutator() {
    global().calculateHeads.add(markCopyLocal.getInitialRegion());
    flush();
  }
  
  /****************************************************************************
   *
   * Mutator-time allocation
   */

  /**
   * Allocate space (for an object)
   *
   * @param bytes The size of the space to be import org.mmtk.utility.deque.AddressDeque;allocated (in bytes)
   * @param align The requested alignment.
   * @param offset The alignment offset.
   * @param allocator The allocator number to be used for this allocation
   * @param site Allocation site
   * @return The address of the first byte of the allocated region
   */
  @Inline
  public Address alloc(int bytes, int align, int offset, int allocator, int site) {
    if (allocator == Compressor.ALLOC_MC) {
      if(markCopyLocal.getInitialRegion().isZero() && 
          Compressor.ready) {
        if(VM.VERIFY_ASSERTIONS) {
          VM.assertions._assert(!global().allocationHeads.isEmpty());
        }
        Address head = global().allocationHeads.pop();
        if(VERBOSE) {
          Log.write("Using collectorHead "); Log.write(head); Log.writeln();
        }
        markCopyLocal.setInitialRegion(head);
      }
      //Log.write(getId()); Log.write(" ");
      return markCopyLocal.alloc(bytes, align, offset);
    }
    
    return super.alloc(bytes, align, offset, allocator, site);
  }

  /**
   * Perform post-allocation actions.  For many allocators none are
   * required.
   *
   * @param object The newly allocated object
   * @param typeRef The type reference for the instance being created
   * @param bytes The size of the space to be allocated (in bytes)
   * @param allocator The allocator number to be used for this allocation
   */
  @Inline
  public void postAlloc(ObjectReference object, ObjectReference typeRef,
      int bytes, int allocator) {
    if (allocator == Compressor.ALLOC_MC) 
      return;
    
    super.postAlloc(object, typeRef, bytes, allocator);
  }

  /**
   * Return the allocator instance associated with a space
   * <code>space</code>, for this plan instance.
   *
   * @param space The space for which the allocator instance is desired.
   * @return The allocator instance associated with this plan instance
   * which is allocating into <code>space</code>, or <code>null</code>
   * if no appropriate allocator can be established.
   */
  public Allocator getAllocatorFromSpace(Space space) {
//    Log.write("getAllocatorFromSpace "); Log.write(space.getName()); Log.writeln();
//    Log.write("ID "); Log.write(getId()); Log.writeln();
//    
//    if (space == Compressor.toSpace()) 
//      return markCopyLocal;
//    
//    if (VM.VERIFY_ASSERTIONS) {
//      VM.assertions._assert(space != Compressor.fromSpace());
//    }
//    
    if(space == Compressor.markCopySpace0 || space == Compressor.markCopySpace1) {
      return markCopyLocal;
    }
    
    return super.getAllocatorFromSpace(space);
  }

  /****************************************************************************
   *
   * Collection
   */

  /**
   * Perform a per-mutator collection phase.
   *
   * @param phaseId The collection phase to perform
   * @param primary Perform any single-threaded activities using this thread.
   */
  @Inline
  public void collectionPhase(short phaseId, boolean primary) {
    if (phaseId == Compressor.PREPARE) {
      super.collectionPhase(phaseId, primary);
      if(VERBOSE) {
        Log.write("Initial region "); Log.write(markCopyLocal.getInitialRegion()); Log.writeln();
      }
      // Mark last region in the bump pointer with it's end
      markCopyLocal.prepare();
      return;
    }
    
    if(phaseId == Compressor.PUSH_MUTATOR_HEADS) {      
      if (!markCopyLocal.getInitialRegion().isZero()) {
        global().calculateHeads.add(markCopyLocal.getInitialRegion());
        if(VERBOSE) {
          Log.write("Pushing "); Log.write(markCopyLocal.getInitialRegion()); Log.writeln();
        }
      }
      return;
    }

    if (phaseId == Compressor.RELEASE) {
      super.collectionPhase(phaseId, primary);   
      // Rebind the allocation bump pointer to the appropriate semispace.
      Log.write("Prev space "); Log.write(markCopyLocal.getSpace().getName()); Log.writeln();
      markCopyLocal.rebind(Compressor.toSpace());
      Log.write("Rebound to space "); Log.write(markCopyLocal.getSpace().getName()); Log.writeln();
      
      return;
    }
    

    super.collectionPhase(phaseId, primary);
  }  

  /****************************************************************************
   *
   * Miscellaneous
   */
  
  /** @return The active global plan as an <code>SS</code> instance. */
  @Inline
  private static Compressor global() {
    return (Compressor) VM.activePlan.global();
  }

  /**
   * Show the status of each of the allocators.
   */
  public final void show() {
    markCopyLocal.show();
    los.show();
    immortal.show();
  }

  /**
   * Flush the pages this mutator has allocated back to the global
   * dirty page list, where the collectors can find them.
   *
   * @see org.mmtk.plan.MutatorContext#flush()
   */
  @Override
  public void flush() {
    super.flush();
    Log.write("Region after first("); Log.write(markCopyLocal.getInitialRegion()); Log.write(") ");
    Log.write(MarkCopyLocal.getNextRegion(markCopyLocal.getInitialRegion()));
    Log.writeln();
    markCopyLocal.setDataEnd();
  }
  
}
